﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace KSASupport.Calibration
{
    /// <summary>
    /// Each pixel has a range of depth values over time. This pixel is just a storage mechanism so we can analyze these values and try to determine which are 'correct'. 
    /// </summary>
    public class CalibrationPixel
    {
        // Store values for 'depth value' and 'number of times we see it over time' in a dictionary
        // We can then query later to find out which value is highest, and store that key as the 'actual depth'
        // to measure against.
        // Depth Value , Hits
        private Dictionary<int, int> buckets;
        /// <summary>
        /// Exposes the raw buckets from the calibration. This is used for calibration apps to tweak
        /// the existing calibration settings.
        /// </summary>
        public Dictionary<int, int> Buckets
        {
            get { return buckets; }
            set { buckets = value; }
        }

        private int samples;
        /// <summary>
        /// Samples is highest number of times we consistently see a value. 
        /// </summary>
        public int Samples
        {
            get { return samples; }
            set { samples = value; }
        }

        private int value;
        /// <summary>
        /// Calculated value for this depth pixel
        /// </summary>
        public int Value
        {
            get 
            {
                return this.value; 
            }
            set { this.value = value; }
        }

        /// <summary>
        /// The calibration system uses the depthmanager settings class to store/retrieve saved values
        /// </summary>
        private DepthManagerSettings depthManagerSettings;

        /// <summary>
        /// Instantiates a new calibratiob pixel
        /// </summary>
        /// <param name="initialValue">The first value to store for a particular pixel</param>
        /// <param name="settings">the depthmanager which to retrieve settings for this pixel</param>
        public CalibrationPixel(int initialValue,DepthManagerSettings settings)
        {
            buckets = new Dictionary<int, int>();
            buckets.Add(initialValue, 1);
            this.value = initialValue;
            depthManagerSettings = settings;
        }

        /// <summary>
        /// Adds the submitted value to the calibration matrix. If enough samples are given, states the pixel is calibrated
        /// </summary>
        /// <param name="distance">distance from Kinect to the screen</param>
        /// <returns>True if the pixel is calibrated, false if not</returns>
        public bool Add(int distance)
        {
            if (samples > 300)
            {
                return true;
            }
                

            if (buckets.Keys.Contains(distance))
            {
                buckets[distance]++;
                samples++;
            }
            else
            {
                buckets.Add(distance, 1);
            }
            CompleteCalibration();
            return false;
        }

        /// <summary>
        /// Return the distance a given input is from the 'surface'. This will help determine how far fingers,hands,
        /// etc are from the Surface.
        /// </summary>
        /// <param name="distance">The distance away from the kinect you want to measure</param>
        /// <returns>-1 if unknown, otherwise the distance from the Surface.</returns>
        public int GetDistanceFromCalibratedPixel(int distance)
        {

            // Below is a mockup using sample values to help understand the Kinects Perspective
            // The values on the left are demonstration values, followed by the property that tracks it.
            //
            // Kinect Sensor ----------------[====0==0=0====]
            //
            //                  Depth area (DistanceOverThreshold)
            //
            //                                       (Finger)
            // High Value ---------------------------/-/---------2029 (depthManagerSettings.HighestDepthFromSurface)
            //               DepthCutoffThreshold   / /
            // Low Value --------------------------/-/---------2036 (depthManagerSettings.LowestDepthFromSurface)
            //                                    /_/
            // Surface   ______________________________________2046 (value)
            //

            
            if ((this.value < 0) || (distance < 0))
                return -1;

            if (buckets.Keys.Contains(distance))
            {
                // Note: Ideally we'd store off more than one value per pixel since we get several.
                // In the future we'll look at modifying this.
                //if (buckets[distance] > 20)
                //{
                    //return 0;
                //}
            }
            int distanceFromCalibrationPoint = this.value - distance;
            //if ((distanceFromCalibrationPoint > 0) && (distanceFromCalibrationPoint < 100))
            if ((distanceFromCalibrationPoint > 0))
            {
                return distanceFromCalibrationPoint;
            }
            else
            {
                return 0;
            }
            
        }

        /// <summary>
        /// Finalizes calibration for this pixel
        /// </summary>
        public void CompleteCalibration()
        {
            // Note: This is one area to tweak settings when trying to get a better calibration
            //int val = buckets.Values.Max();
            this.value = buckets.Keys.Min();
            //this.value = buckets[val];

        }
    }
}
